/*
 * @Author: your name
 * @Date: 2020-04-27 11:36:25
 * @LastEditTime: 2020-08-04 12:51:55
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \rec_rec\platform\cva\file_read.c
 */

/**
 * @Date: 2020-04-27 11:02:32
 * @Description: 
 * @Return: 
 */
#include <signal.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <string.h>
#include "read_ps_file.h"
#include "global_common.h"

static int FindStartCode (unsigned char *Buf){
	if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=1 || Buf[3] !=0xba) return 0;//0x000001BA?
	else return 1;
}

//
/**
 * @Date: 2020-04-27 13:54:53
 * @Description: i 帧和 比软件 多 136个字节,软件不考虑bb bc
 * @Return: 
 */
int GetAnnextPS (PS_t *ps_unit){
	int pos = 0;
	int StartCodeFound, rewind;
	int StartCodeFound_00;
	unsigned char *Buf;

	if ((Buf = (unsigned char*)calloc (ps_unit->max_size , sizeof(char))) == NULL) {
		xeLOG_WARNING("GetAnnexbNALU: Could not allocate Buf memory\n");
		sleep(2);
		exit(-1);
	}


	if (4 != fread (Buf, 1, 4, ps_unit->psbitstream)){
		free(Buf);
		return 0;
	}
	pos = 4;
	
	StartCodeFound_00 =  FindStartCode(Buf);
	if(!StartCodeFound_00){
		// STD_print("error not found ps head\n\n");
	}

	StartCodeFound = 0;

	while (!StartCodeFound){
		if (feof (ps_unit->psbitstream)){
			ps_unit->len = pos-1;
			memcpy (ps_unit->buf, &Buf[0], ps_unit->len);     
			free(Buf);
			return ps_unit->len;
		}
		int m = fgetc(ps_unit->psbitstream);
		// if(ps_num > 60843)
		// 	STD_print("mmm 0x%x",m);
		if(pos == ps_unit->max_size){
			xeLOG_WARNING("get 1M.can not find ps header");
			ps_unit->len = pos-1;
			memcpy (ps_unit->buf, &Buf[0], ps_unit->len);     
			free(Buf);
			return ps_unit->len;	
		}
		Buf[pos++] = (unsigned char)m;
		StartCodeFound = FindStartCode(&Buf[pos-4]);
	}

	// Here, we have found another start code (and read length of startcode bytes more than we should
	// have.  Hence, go back in the file
	rewind = -4;

	if (0 != fseek (ps_unit->psbitstream, rewind, SEEK_CUR)){
		free(Buf);
		printf("GetAnnexbNALU: Cannot fseek in the bit stream file");
	}

	ps_unit->len = pos+rewind;
	memcpy (ps_unit->buf, &Buf[0], ps_unit->len);
	free(Buf);

	return ps_unit->len;
}



int ps_read_init(PS_t *ps_unit,char *file_name)
{
	if(ps_unit->ps_proc_init)
		return 0;
    ps_unit->psbitstream=fopen(file_name, "rb");
	if ( ps_unit->psbitstream==NULL){
		printf("Open file error\n");
		return -1;
	}

	ps_unit->max_size=PS_MAX_LEN;
	ps_unit->buf = (char*)calloc(PS_MAX_LEN, sizeof (char));
	if (ps_unit->buf == NULL){
		STD_print ("AllocNALU: n->buf failed");
		return -1;
	}
	ps_unit->ps_proc_init =1;
    return 0;
}
int ps_read_one_frame(PS_t *ps_unit,char **out_buf,int *out_len)
{
    if(!ps_unit->ps_proc_init)
        return -1;

    if(!feof(ps_unit->psbitstream)){
		int data_lenth;
		data_lenth=GetAnnextPS(ps_unit);

		// fprintf(stdout,"num: %5d|offset: %ld|length: %8d|\n",
        //     ps_unit->ps_num,ps_unit->total_offset,ps_unit->len);
		ps_unit->total_offset +=data_lenth;
		ps_unit->ps_num++;

    }else{
        STD_print("read end");
        return -1;
    }
    *out_buf = ps_unit->buf;
    *out_len = ps_unit->len;
    return 0;
}
int ps_read_deinit(PS_t *ps_unit)
{

	if (ps_unit->buf){
		free(ps_unit->buf);
		ps_unit->buf=NULL;
	}

    if(ps_unit->psbitstream){
        fclose(ps_unit->psbitstream);
		ps_unit->psbitstream = NULL;
    }
	ps_unit->ps_proc_init =0;
	return 0;
}


typedef struct
{
	uint8_t M1:1;
	uint8_t TS_30_32:3;
	uint8_t PTS_DTS_flags:4;

	uint8_t TS_22_29;

	uint8_t M2:1;
	uint8_t TS_15_21:7;

	uint8_t TS_7_14;

	uint8_t M3:1;
	uint8_t TS_0_6:7;
}__attribute__((packed)) _Demux_PS_PES_TS_t;


typedef enum {
	NALU_TYPE_SLICE    = 1,
	NALU_TYPE_DPA      = 2,
	NALU_TYPE_DPB      = 3,
	NALU_TYPE_DPC      = 4,
	NALU_TYPE_IDR      = 5,
	NALU_TYPE_SEI      = 6,
	NALU_TYPE_SPS      = 7,
	NALU_TYPE_PPS      = 8,
	NALU_TYPE_AUD      = 9,
	NALU_TYPE_EOSEQ    = 10,
	NALU_TYPE_EOSTREAM = 11,
	NALU_TYPE_FILL     = 12,
} NaluType;

typedef enum {
	NALU_PRIORITY_DISPOSABLE = 0,
	NALU_PRIRITY_LOW         = 1,
	NALU_PRIORITY_HIGH       = 2,
	NALU_PRIORITY_HIGHEST    = 3
} NaluPriority;

#define PACK_START_CODE             ((unsigned int)0x000001ba)
#define SYSTEM_HEADER_START_CODE    ((unsigned int)0x000001bb)

/* mpeg2 */
#define PROGRAM_STREAM_MAP 0x1bc
#define PRIVATE_STREAM_1   0x1bd
#define PADDING_STREAM     0x1be
#define PRIVATE_STREAM_2   0x1bf


#define PROGRAM_STREAM_PES_VIDEO 0x1e0
#define PROGRAM_STREAM_PES_AUDIO 0x1c0

#define AUDIO_ID 0xc0
#define VIDEO_ID 0xe0


#define AV_RB16(x)                           \
    ((((const uint8_t*)(x))[0] << 8) |          \
      ((const uint8_t*)(x))[1])

static inline int64_t ff_parse_pes_pts(const uint8_t *buf) {
    return (int64_t)(*buf & 0x0e) << 29 |
            (AV_RB16(buf+1) >> 1) << 15 |
             AV_RB16(buf+3) >> 1;
}

typedef  unsigned long xerec_off_t;
typedef struct
{
	int  date_type;
	time_t birthsec_pts;
	xerec_off_t data_offset;
}__attribute__ ((packed)) xerec_file_idx_t;


static int find_next_start_code(char *buf, int *size_ptr,
                                 int *header_state)
{
    int  state=0, v=0;
    int val, n;
	char *p_buf = (char *)buf;
    state = *header_state;
    n     = *size_ptr;
	// STD_print("left:%d",n);
    while (n > 0) {
        v = (*p_buf++)&(0xff);
        n--;
        if (state == 0x000001) {
            state = ((state << 8) | v) & 0xffffff;
            val   = state;
            goto found;
        }
        state = ((state << 8) | v) & 0xffffff;
    }
    val = -1;

found:
    *header_state = state;
    *size_ptr     = n;
    return val;
}


int ps_prase (PS_t *ps_unit)
{
	int startcode,size_left;
	int header_state = 0xff;
	int seek_pos = 0;
	size_left      = ps_unit->len;

redo:
	header_state = 0xff;
    startcode = find_next_start_code((char *)ps_unit->buf+seek_pos, &size_left, &header_state);
    seek_pos = ps_unit->len - size_left;
    if (startcode < 0) {
		// STD_print("\tend\n");
		return 0;
    }
	//STD_print("tinybear startcode %d 0x%x\n",seek_pos,startcode);

    if (startcode == PACK_START_CODE){
		// STD_print("PACK_START_CODE");
		char *p_buf = (char *)ps_unit->buf+seek_pos;
		int paddint_len = 0;
		paddint_len = (p_buf[9])&0x07;
		seek_pos += (10+paddint_len);
		size_left -= (10+paddint_len);
        goto redo;
	}
    if (startcode == SYSTEM_HEADER_START_CODE || startcode == PROGRAM_STREAM_MAP){
		// STD_print("SYSTEM_HEADER_START_CODE or PROGRAM_STREAM_MAP");
		char *p_buf = (char *)ps_unit->buf+seek_pos;
		int pes_len = 0;
		pes_len = AV_RB16(p_buf);
		seek_pos += (2+pes_len);
		size_left -= (2+pes_len);
        goto redo;
	}

    /* find matching stream */
    if (!((startcode >= 0x1c0 && startcode <= 0x1df) ||
          (startcode >= 0x1e0 && startcode <= 0x1ef) ||
          (startcode == 0x1bd) ||
          (startcode == PRIVATE_STREAM_2) ||
          (startcode == 0x1fd))){
		
        goto redo;
	}else{
		//  STD_print("tt startcode %x",startcode);
		if (startcode == PROGRAM_STREAM_PES_VIDEO || startcode == PROGRAM_STREAM_PES_AUDIO) {
			// STD_print("PROGRAM_STREAM_PES");
			uint8_t *p_buf = ( uint8_t *)ps_unit->buf+seek_pos;
			int flags= p_buf[3];
			int pes_len = 0;
			// STD_print("pes Len:%d",pes_len = AV_RB16(p_buf));
			if (flags & 0x80) {
					int64_t pts;
					pts = ff_parse_pes_pts(p_buf+5);
					// STD_print("%.3f",(double)((double)(pts)/90000));
					if(startcode == PROGRAM_STREAM_PES_VIDEO){
						ps_unit->v_pts = (double)(pts)/90000;
					}else{
						ps_unit->a_pts = (double)(pts)/90000;
					}
			}
			ps_unit->video_audio |= (startcode == PROGRAM_STREAM_PES_VIDEO) ? ps_type_video:ps_type_audio;
			seek_pos += (2+pes_len);
			size_left -= (2+pes_len);
			goto redo;
		}
		goto redo;
	}
	return 0;
}


int my_ps_prase(const char *file_in,const char *file_out)
{
	int retval;
	PS_t *ps_unit;
	FILE *myout=stdout;
	int data_offset=0;
	int ps_num=0;
	time_t birthsec_base = 0;
	xerec_file_idx_t idx;
	int sec_num=0;
	unsigned int cur_time_pts = 0;
	xerec_file_idx_t g_idx[86400];

	ps_unit = (PS_t*)calloc (1, sizeof (PS_t));
	if (ps_unit == NULL){
		printf("Alloc NALU Error\n");
		return 0;
	}

	ps_unit->max_size=PS_MAX_LEN;
	ps_unit->buf = (char*)calloc (PS_MAX_LEN, sizeof (char));
	if (ps_unit->buf == NULL){
		free (ps_unit);
		printf ("AllocNALU: n->buf");
		return -1;
	}
	ps_unit->psbitstream=fopen(file_in, "rb");
	if (ps_unit->psbitstream==NULL){
		printf("Open file error\n");
		return 0;
	}


	while(!feof(ps_unit->psbitstream)) 
	{
		int data_lenth;
		ps_unit->v_pts = ps_unit->a_pts = ps_unit->video_audio = 0;
		memset(ps_unit->buf,0, PS_MAX_LEN);
		//get one ps packet
		data_lenth=GetAnnextPS(ps_unit);
		// fprintf(myout,"\n\tnum: %5d|offset: %8d|length: %8d|\n",ps_num,data_offset,ps_unit->len);

		//get detail info
		ps_prase(ps_unit);
		if((ps_unit->video_audio & ps_type_mix) == ps_type_audio){
			// xeLOG_DEBUG("audio pts:%.3f",ps_unit->a_pts);
		}else if ((ps_unit->video_audio & ps_type_video) == ps_type_video){
			// xeLOG_DEBUG("%s pts:%.3f",(ps_unit->video_audio == ps_type_video)?"video":"mixer",ps_unit->v_pts);
			if(cur_time_pts == 0){
				birthsec_base = (unsigned int)(ps_unit->v_pts);
				// STD_print("base base:")
			}
			if(cur_time_pts != (unsigned int)(ps_unit->v_pts)){
					idx.birthsec_pts = (unsigned int)(ps_unit->v_pts);
					idx.data_offset = data_offset;
					idx.date_type = 0;
					memcpy(&g_idx[idx.birthsec_pts - birthsec_base], &idx , sizeof(xerec_file_idx_t));
					sec_num++;
					// xeLOG_DEBUG("idx.birthsec_pts:%d, idx.data_offset:%d sec_num:%d",(int)idx.birthsec_pts,idx.data_offset,sec_num);
					cur_time_pts = (unsigned int)(ps_unit->v_pts);
			}
		}else{
			// xeLOG_DEBUG("other type");
		}

		data_offset += data_lenth;
		ps_num++;
	}
	xeLOG_INFO("%s,%d sec detected sec_num:%d\n",file_out,(int)(idx.birthsec_pts - birthsec_base+1),sec_num);
	{
		int ret;
		FILE * my_detail=fopen(file_out, "wb+");
		if ( my_detail==NULL){
			printf("Open file error\n");
			return -1;
		}
		ret = fwrite(g_idx, sizeof(xerec_file_idx_t), sec_num, my_detail);
		// printf("fwrite ret:%d",ret);
		fclose(my_detail);
	}
	sync();
	// sleep(1);
	// {
	// 	int ret;
	// 	int i;
	// 	char buf[512];
	// 	xerec_file_idx_t *p_idx = (xerec_file_idx_t *)buf;
	// 	FILE * my_detail=fopen(file_out, "rb");
	// 	if ( my_detail==NULL){
	// 		printf("Open file error\n");
	// 		return -1;
	// 	}
	// 	for(i = 0; i <sec_num;i++){
	// 		ret = fread(buf, 1,sizeof(xerec_file_idx_t), my_detail);
	// 		// printf("\ti:%d ret:%d type:%d pts:%ld offset:%d\n",i,ret,
	// 		// 	p_idx->date_type,p_idx->birthsec_pts,p_idx->data_offset);
	// 	}
	// 	fclose(my_detail);
	// }
	// sync();
	// sleep(1);

	xeLOG_NOTICE("sec_num:%d per_idx:%ld filesize:%lld\n",
		sec_num,sizeof(xerec_file_idx_t),get_file_size(file_out));

	if (ps_unit){
		if (ps_unit->buf){
			free(ps_unit->buf);
			ps_unit->buf=NULL;
		}
		free (ps_unit);
	}

	return sec_num;
}

long long get_offset_by_sec(char *file_name,char *detail_name,int sec_offset,int start)
{
	int ret;

	int sec_total = 0;
	char buf[512]={0};
	xerec_file_idx_t *p_idx = (xerec_file_idx_t *)buf;
	
	sec_total = get_file_size(detail_name)/sizeof(xerec_file_idx_t);
	if(sec_total == 0){
		xeLOG_ERR("");
		return -1;
	}

	if(start){
		sec_offset = sec_offset;
	}else{
		sec_offset = sec_offset + 1;
	}
	if(sec_offset > (sec_total-1)){
		return get_file_size(file_name);
	}

	xeLOG_WARNING("sec_offset:%d sec_total:%d",sec_offset,sec_total);
	FILE * my_detail=fopen(detail_name, "rb");
	if ( my_detail==NULL){
		printf("Open file error\n");
		return -1;
	}
	fseek(my_detail,sizeof(xerec_file_idx_t)*sec_offset,SEEK_SET);
	ret = fread(buf, 1,sizeof(xerec_file_idx_t), my_detail);
	fclose(my_detail);

	return p_idx->data_offset;

}